create-eth
Version:
Create a Scaffold-ETH-2 app
140 lines (112 loc) • 4.6 kB
JavaScript
import { withDefaults } from "../../../../../../../utils.js";
const contents = ({ solidityFramework, artifactsDirName }) => {
if (!solidityFramework[0]) {
solidityFramework[0] = "hardhat";
}
const isFoundry = solidityFramework[0] === "foundry";
return `
import fs from "fs";
import path from "path";
import { Address } from "viem";
import { ${solidityFramework[0]} } from "viem/chains";
import { AddressComponent } from "~~/app/blockexplorer/_components/AddressComponent";
import deployedContracts from "~~/contracts/deployedContracts";
import { isZeroAddress } from "~~/utils/scaffold-eth/common";
import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract";
type PageProps = {
params: Promise<{ address: Address }>;
};
${
isFoundry
? `function fetchByteCodeAndAssembly(foundryOutDirectory: string, contractName: string) {
// Foundry organizes artifacts by source file name, not contract name.
// Try the default path first, then fall back to scanning all .sol dirs (skipping build-info).
let artifactPath = path.join(foundryOutDirectory, \`\${contractName}.sol\`, \`\${contractName}.json\`);
if (!fs.existsSync(artifactPath)) {
artifactPath = "";
const solDirs = fs.readdirSync(foundryOutDirectory).filter(entry => entry.endsWith(".sol"));
for (const solDir of solDirs) {
const candidate = path.join(foundryOutDirectory, solDir, \`\${contractName}.json\`);
if (fs.existsSync(candidate)) {
artifactPath = candidate;
break;
}
}
}
if (!artifactPath) {
return { bytecode: "", assembly: "" };
}
const artifact = JSON.parse(fs.readFileSync(artifactPath, "utf8"));
const bytecode: string = artifact?.bytecode?.object ?? "";
const assembly: string = artifact?.opcodes ?? artifact?.bytecode?.opcodes ?? "";
return { bytecode, assembly };
}`
: `async function fetchByteCodeAndAssembly(buildInfoDirectory: string, contractName: string) {
const contractPath = \`project/contracts/\${contractName}.sol\`;
const buildInfoFiles = fs.readdirSync(buildInfoDirectory).filter(f => f.endsWith(".output.json"));
let bytecode = "";
let assembly = "";
for (let i = 0; i < buildInfoFiles.length; i++) {
const filePath = path.join(buildInfoDirectory, buildInfoFiles[i]);
const buildInfo = JSON.parse(fs.readFileSync(filePath, "utf8"));
if (buildInfo.output?.contracts?.[contractPath]) {
for (const contract in buildInfo.output.contracts[contractPath]) {
bytecode = buildInfo.output.contracts[contractPath][contract].evm.bytecode.object;
assembly = buildInfo.output.contracts[contractPath][contract].evm.bytecode.opcodes;
break;
}
}
if (bytecode && assembly) {
break;
}
}
return { bytecode, assembly };
}`
}
const getContractData = async (address: Address) => {
const contracts = deployedContracts as GenericContractsDeclaration | null;
const chainId = ${solidityFramework[0]}.id;
if (!contracts || !contracts[chainId] || Object.keys(contracts[chainId]).length === 0) {
return null;
}
const artifactsDirectory = path.join(
process.cwd(),
"..",
"${solidityFramework[0]}",
"${artifactsDirName[0]}"${isFoundry ? "" : `,
"build-info"`},
);
if (!fs.existsSync(artifactsDirectory)) {
throw new Error(\`Directory \${artifactsDirectory} not found.\`);
}
let matchedContractName = "";
const deployedContractsOnChain = contracts[chainId];
for (const [contractName, contractInfo] of Object.entries(deployedContractsOnChain)) {
if (contractInfo.address.toLowerCase() === address.toLowerCase()) {
matchedContractName = contractName;
break;
}
}
if (!matchedContractName) {
// No contract found at this address
return null;
}
const { bytecode, assembly } = await fetchByteCodeAndAssembly(artifactsDirectory, matchedContractName);
return { bytecode, assembly };
};
export function generateStaticParams() {
// An workaround to enable static exports in Next.js, generating single dummy page.
return [{ address: "0x0000000000000000000000000000000000000000" }];
}
const AddressPage = async (props: PageProps) => {
const params = await props.params;
const address = params?.address as Address;
if (isZeroAddress(address)) return null;
const contractData: { bytecode: string; assembly: string } | null = await getContractData(address);
return <AddressComponent address={address} contractData={contractData} />;
};
export default AddressPage;`;
};
export default withDefaults(contents, {
artifactsDirName: "artifacts",
});